package com.gz.common.mongodb.dao;

import com.gz.common.mongodb.support.MongoField;
import com.gz.common.mongodb.utils.Mongo;
import com.gz.common.mongodb.base.MongoBaseModel;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.lang.NonNull;
import org.springframework.lang.Nullable;

import javax.validation.constraints.NotNull;
import java.util.List;

/**
 * @ClassName MongoQueryMapper
 * @Description // 提供文档查询的方法
 * @Author WHX
 * @Date 2021/9/19 20:34
 **/
public interface MongoQueryMapper<Model, ID> extends MongoGetTClass<Model> {
    /**
     * 主键ID查询
     * @param id
     * @return
     */
    default Model selectByPrimaryKey(ID id){
        return this.findOne(Query.query(Criteria.where("id").is(id)));
    }

    /**
     * 根据条件查询
     *
     * @param query
     * @return
     */
    default List<Model> find(@NonNull Query query) {
        return Mongo.mongoTemplate().find(query, this.getTClass());
    }

    default List<Model> find(@NonNull Criteria criteria) {
        return this.find(Query.query(criteria));
    }

    default List<Model> find(@NonNull Criteria criteria, @Nullable Sort sort, @Nullable MongoField mongoField) {
        Query query = Query.query(criteria);
        Mongo.buildFields(query.fields(), mongoField);
        if (sort != null) query.with(sort);
        return this.find(query);
    }

    /**
     * 根据实体类中不为null的所有字段（字段名--字段值）作为查询条件
     * <p>
     * 注意：仅支持的字段类型为：基本类型，或基本类型的包装类，或字符串
     * 构建条件时，会忽略集合类型以及其他引用类型
     *
     * @param model
     * @return
     */
    default List<Model> findSelective(@Nullable MongoBaseModel model) {
        return this.findSelective(model, null, null);
    }

    /**
     * findSelective方法 + 排序 + 排除/包含字段
     *
     * @param model
     * @param sort
     * @param mongoField
     * @return
     */
    default List<Model> findSelective(@Nullable MongoBaseModel model, @Nullable Sort sort, @Nullable MongoField mongoField) {
        return this.find(Mongo.getSelectiveCriteria(model), sort, mongoField);
    }

    /**
     * 根据条件查询第一条数据
     * （注意，这里数据不唯一时，默认是只返回第一条数据）
     *
     * @param query
     * @return
     */
    default Model findOne(Query query) {
        return Mongo.mongoTemplate().findOne(query, this.getTClass());
    }

    /**
     * 根据实体类中不为null的所有字段（字段名--字段值）作为查询条件
     * <p>
     * 注意：仅支持的字段类型为：基本类型，或基本类型的包装类，或字符串
     * 构建条件时，会忽略集合类型以及其他引用类型
     *
     * @param model
     * @return
     */
    default Model findOne(@Nullable MongoBaseModel model) {
        return this.findOne(model, null, null);
    }


    /**
     * findOne方法 + 排序
     * @param model
     * @param sort
     * @return
     */
    default Model findOne(@Nullable MongoBaseModel model, @Nullable Sort sort) {
        return this.findOne(model, sort, null);
    }


    /**
     * findOne方法 + 排除/包含字段
     *
     * @param model
     * @param mongoField
     * @return
     */
    default Model findOne(@Nullable MongoBaseModel model, @Nullable MongoField mongoField) {
        return this.findOne(model, null, mongoField);
    }

    /**
     * findOne方法 + 排序 + 排除/包含字段
     * @param model
     * @param sort
     * @param mongoField
     * @return
     */
    default Model findOne(@Nullable MongoBaseModel model, @Nullable Sort sort, @Nullable MongoField mongoField) {
        Query query = Query.query(Mongo.getSelectiveCriteria(model));
        Mongo.buildFields(query.fields(), mongoField);
        if (sort != null) query.with(sort);
        return this.findOne(query);
    }

    /**
     * findOne方法 + 排除/包含字段
     *
     * @param criteria
     * @param mongoField
     * @return
     */
    default Model findOne(@NotNull Criteria criteria, @Nullable MongoField mongoField) {
        Query query = Query.query(criteria);
        Mongo.buildFields(query.fields(), mongoField);
        return this.findOne(query);
    }

    /**
     * findOne方法
     *
     * @param criteria
     * @return
     */
    default Model findOne(@NotNull Criteria criteria) {
        return this.findOne(Query.query(criteria));
    }

}
